// Grading Service - AI 批改服务

use axum::{
    extract::{Json, Path, State},
    http::StatusCode,
    routing::{get, post, patch},
    Router,
};
use common::{ApiResponse, UserRole};
use serde::{Deserialize, Serialize};
use sqlx::PgPool;
use std::sync::Arc;
use auth::AuthUser;

// ========== 数据模型 ==========

#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
struct GradingResult {
    id: String,
    submission_id: String,
    total_score: f64,
    max_score: f64,
    question_details: serde_json::Value,
    ai_confidence: Option<f64>,
    status: String,
    graded_at: chrono::DateTime<chrono::Utc>,
}

#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
struct WrongAnswer {
    id: String,
    student_id: String,
    homework_id: String,
    question_number: i32,
    question_content: String,
    student_answer: Option<String>,
    correct_answer: String,
    error_type: Option<String>,
    knowledge_points: Vec<String>,
    explanation: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, sqlx::FromRow)]
struct GradingTask {
    id: String,
    submission_id: String,
    status: String,
    priority: i32,
    retry_count: i32,
    created_at: chrono::DateTime<chrono::Utc>,
}

// ========== API 请求/响应 ==========

#[derive(Debug, Deserialize)]
struct StartGradingRequest {
    submission_id: String,
    priority: Option<i32>,  // 0-10, 默认 0
}

#[derive(Debug, Serialize)]
struct StartGradingResponse {
    task_id: String,
    estimated_time: u32,  // 估算时间（秒）
}

#[derive(Debug, Deserialize)]
struct ReviewRequest {
    question_adjustments: Vec<QuestionAdjustment>,
    review_notes: Option<String>,
}

#[derive(Debug, Deserialize, Serialize)]
struct QuestionAdjustment {
    question_number: i32,
    new_score: f64,
    reason: String,
}

// ========== 应用状态 ==========

#[derive(Clone)]
struct AppState {
    db: PgPool,
    deepseek_api_key: String,
    rabbitmq_url: String,
}

// ========== 处理器 ==========

/// 健康检查
async fn health_check() -> (StatusCode, Json<ApiResponse<String>>) {
    (StatusCode::OK, Json(ApiResponse::ok("OK".to_string())))
}

/// 开始批改（创建任务并入队）
async fn start_grading(
    auth_user: AuthUser,
    State(state): State<Arc<AppState>>,
    Json(req): Json<StartGradingRequest>,
) -> (StatusCode, Json<ApiResponse<StartGradingResponse>>) {
    // 检查权限：教师或管理员
    if let Err(_) = auth::require_role(&auth_user, &[UserRole::Teacher, UserRole::Admin]) {
        return (
            StatusCode::FORBIDDEN,
            Json(ApiResponse::error("仅教师可以发起批改".to_string())),
        );
    }

    // 验证提交是否存在
    let submission_exists: bool = sqlx::query_scalar(
        "SELECT EXISTS(SELECT 1 FROM homework_submissions WHERE id = $1)"
    )
    .bind(&req.submission_id)
    .fetch_one(&state.db)
    .await
    .unwrap_or(false);

    if !submission_exists {
        return (
            StatusCode::NOT_FOUND,
            Json(ApiResponse::error("作业提交不存在".to_string())),
        );
    }

    // 创建批改任务
    let task_id = common::generate_id();
    let priority = req.priority.unwrap_or(0).max(0).min(10);

    let result = sqlx::query(
        r#"
        INSERT INTO grading_tasks (id, submission_id, priority)
        VALUES ($1, $2, $3)
        "#
    )
    .bind(&task_id)
    .bind(&req.submission_id)
    .bind(priority)
    .execute(&state.db)
    .await;

    match result {
        Ok(_) => {
            // TODO: 发送到 RabbitMQ 队列
            tracing::info!("Grading task created: {}", task_id);
            
            // 估算时间：30秒/份作业
            let estimated_time = 30;

            (
                StatusCode::ACCEPTED,
                Json(ApiResponse::ok(StartGradingResponse {
                    task_id,
                    estimated_time,
                })),
            )
        }
        Err(e) => {
            tracing::error!("Failed to create grading task: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(ApiResponse::error("创建批改任务失败".to_string())),
            )
        }
    }
}

/// 查询批改结果
async fn get_grading_result(
    _auth_user: AuthUser,
    State(state): State<Arc<AppState>>,
    Path(submission_id): Path<String>,
) -> (StatusCode, Json<ApiResponse<GradingResult>>) {
    let result = sqlx::query_as::<_, GradingResult>(
        "SELECT * FROM grading_results WHERE submission_id = $1"
    )
    .bind(&submission_id)
    .fetch_optional(&state.db)
    .await;

    match result {
        Ok(Some(r)) => (StatusCode::OK, Json(ApiResponse::ok(r))),
        Ok(None) => (
            StatusCode::NOT_FOUND,
            Json(ApiResponse::error("批改结果不存在".to_string())),
        ),
        Err(e) => {
            tracing::error!("Database error: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(ApiResponse::error("查询失败".to_string())),
            )
        }
    }
}

/// 查询学生错题
async fn get_wrong_answers(
    auth_user: AuthUser,
    State(state): State<Arc<AppState>>,
    Path(student_id): Path<String>,
) -> (StatusCode, Json<ApiResponse<Vec<WrongAnswer>>>) {
    // 学生只能查询自己的错题
    if auth_user.role == UserRole::Student && auth_user.user_id != student_id {
        return (
            StatusCode::FORBIDDEN,
            Json(ApiResponse::error("无权查询其他学生的错题".to_string())),
        );
    }

    let wrong_answers = sqlx::query_as::<_, WrongAnswer>(
        "SELECT * FROM wrong_answers WHERE student_id = $1 ORDER BY created_at DESC LIMIT 100"
    )
    .bind(&student_id)
    .fetch_all(&state.db)
    .await;

    match wrong_answers {
        Ok(answers) => (StatusCode::OK, Json(ApiResponse::ok(answers))),
        Err(e) => {
            tracing::error!("Failed to fetch wrong answers: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(ApiResponse::error("查询失败".to_string())),
            )
        }
    }
}

/// 教师复核批改结果
async fn review_grading(
    auth_user: AuthUser,
    State(state): State<Arc<AppState>>,
    Path(result_id): Path<String>,
    Json(req): Json<ReviewRequest>,
) -> (StatusCode, Json<ApiResponse<String>>) {
    // 检查权限：仅教师
    if let Err(_) = auth::require_role(&auth_user, &[UserRole::Teacher, UserRole::Admin]) {
        return (
            StatusCode::FORBIDDEN,
            Json(ApiResponse::error("仅教师可以复核".to_string())),
        );
    }

    // 获取原始批改结果
    let original: Option<GradingResult> = sqlx::query_as(
        "SELECT * FROM grading_results WHERE id = $1"
    )
    .bind(&result_id)
    .fetch_optional(&state.db)
    .await
    .unwrap_or(None);

    let original = match original {
        Some(r) => r,
        None => {
            return (
                StatusCode::NOT_FOUND,
                Json(ApiResponse::error("批改结果不存在".to_string())),
            );
        }
    };

    // 更新题目分数
    let _question_details = original.question_details.clone();
    let mut new_total_score = 0.0;

    for adj in &req.question_adjustments {
        // 简化版：直接更新 JSONB（实际应该解析并更新具体字段）
        tracing::info!("Adjusting Q{}: {} -> {}", adj.question_number, "old_score", adj.new_score);
        new_total_score += adj.new_score;
    }

    // 更新数据库
    let result = sqlx::query(
        r#"
        UPDATE grading_results 
        SET total_score = $1, status = 'reviewed', 
            reviewed_by = $2, review_notes = $3, reviewed_at = NOW()
        WHERE id = $4
        "#
    )
    .bind(new_total_score)
    .bind(&auth_user.user_id)
    .bind(&req.review_notes)
    .bind(&result_id)
    .execute(&state.db)
    .await;

    match result {
        Ok(_) => {
            tracing::info!("Grading result reviewed by teacher: {}", auth_user.user_id);
            (StatusCode::OK, Json(ApiResponse::ok("复核成功".to_string())))
        }
        Err(e) => {
            tracing::error!("Review failed: {}", e);
            (
                StatusCode::INTERNAL_SERVER_ERROR,
                Json(ApiResponse::error("复核失败".to_string())),
            )
        }
    }
}

// ========== DeepSeek API 集成 ==========

/// 调用 DeepSeek API 进行 OCR 识别
async fn call_deepseek_ocr(image_urls: &[String], api_key: &str) -> anyhow::Result<String> {
    let client = reqwest::Client::new();
    
    // 构建请求（根据 DeepSeek 官方文档格式）
    let request_body = serde_json::json!({
        "model": "deepseek-chat",
        "messages": [
            {
                "role": "system",
                "content": "你是一个专业的 OCR 识别助手，擅长识别学生作业图片中的题目和答案。"
            },
            {
                "role": "user",
                "content": format!("请识别以下图片中的文字内容，包括题目、学生答案等信息。图片 URL: {:?}\n\n请尽可能准确地识别所有文字内容。", image_urls)
            }
        ],
        "stream": false,
        "temperature": 0.1
    });

    let response = client
        .post("https://api.deepseek.com/chat/completions")
        .header("Authorization", format!("Bearer {}", api_key))
        .header("Content-Type", "application/json")
        .json(&request_body)
        .timeout(std::time::Duration::from_secs(30))
        .send()
        .await?;

    if !response.status().is_success() {
        let status = response.status();
        let error_text = response.text().await.unwrap_or_default();
        return Err(anyhow::anyhow!("DeepSeek API error: {} - {}", status, error_text));
    }

    let result: serde_json::Value = response.json().await?;
    let ocr_text = result["choices"][0]["message"]["content"]
        .as_str()
        .unwrap_or("")
        .to_string();

    Ok(ocr_text)
}

/// 调用 DeepSeek API 进行作业批改
async fn call_deepseek_grading(
    ocr_text: &str,
    standard_answers: &serde_json::Value,
    api_key: &str,
) -> anyhow::Result<serde_json::Value> {
    let client = reqwest::Client::new();
    
    let prompt = format!(
        "请根据以下信息批改学生作业：\n\n\
        学生答案（OCR识别结果）：\n{}\n\n\
        标准答案和评分标准：\n{}\n\n\
        请以 JSON 格式返回批改结果，包含：\n\
        - questions: 数组，每道题包含 number, type, student_answer, correct_answer, score, max_score, is_correct, error_type, knowledge_points, explanation\n\
        - confidence: 批改置信度（0-1）\n\n\
        示例格式：{{\"questions\": [{{\"number\": 1, \"score\": 5, ...}}], \"confidence\": 0.95}}",
        ocr_text,
        serde_json::to_string_pretty(standard_answers).unwrap_or_default()
    );

    let request_body = serde_json::json!({
        "model": "deepseek-chat",
        "messages": [
            {
                "role": "system",
                "content": "你是一个专业的作业批改助手，擅长分析学生答案、识别错误类型、提取知识点。请严格按照 JSON 格式返回批改结果。"
            },
            {
                "role": "user",
                "content": prompt
            }
        ],
        "stream": false,
        "temperature": 0.1
    });

    let response = client
        .post("https://api.deepseek.com/chat/completions")
        .header("Authorization", format!("Bearer {}", api_key))
        .header("Content-Type", "application/json")
        .json(&request_body)
        .timeout(std::time::Duration::from_secs(60))
        .send()
        .await?;

    if !response.status().is_success() {
        let status = response.status();
        let error_text = response.text().await.unwrap_or_default();
        return Err(anyhow::anyhow!("DeepSeek API error: {} - {}", status, error_text));
    }

    let result: serde_json::Value = response.json().await?;
    let grading_result_str = result["choices"][0]["message"]["content"]
        .as_str()
        .unwrap_or("{}");
    
    // 尝试解析 JSON，如果失败则构建一个默认结果
    let grading_result: serde_json::Value = serde_json::from_str(grading_result_str)
        .unwrap_or_else(|_| {
            tracing::warn!("Failed to parse DeepSeek response as JSON, using default");
            serde_json::json!({
                "questions": [],
                "confidence": 0.5
            })
        });
    
    Ok(grading_result)
}

// ========== 批改逻辑（核心函数）==========

/// 执行批改（从队列中调用）
async fn process_grading_task(
    db: &PgPool,
    task: &GradingTask,
    deepseek_api_key: &str,
) -> anyhow::Result<()> {
    // 1. 获取作业提交信息
    let submission: (String, serde_json::Value, String) = sqlx::query_as(
        "SELECT hs.student_id, hs.image_urls, h.questions 
         FROM homework_submissions hs 
         JOIN homeworks h ON hs.homework_id = h.id 
         WHERE hs.id = $1"
    )
    .bind(&task.submission_id)
    .fetch_one(db)
    .await?;

    let (student_id, image_urls_json, questions_json) = submission;
    let image_urls: Vec<String> = serde_json::from_value(image_urls_json)?;
    let standard_answers: serde_json::Value = serde_json::from_str(&questions_json).unwrap_or(serde_json::json!({}));

    // 2. 调用 DeepSeek OCR API
    tracing::info!("Calling DeepSeek OCR for submission: {}", task.submission_id);
    let ocr_text = call_deepseek_ocr(&image_urls, deepseek_api_key).await?;
    
    // 3. 调用 DeepSeek AI 批改
    tracing::info!("Calling DeepSeek grading for submission: {}", task.submission_id);
    let question_details = call_deepseek_grading(&ocr_text, &standard_answers, deepseek_api_key).await?;

    // 4. 计算总分和置信度
    let empty_vec = vec![];
    let questions_arr = question_details["questions"].as_array().unwrap_or(&empty_vec);
    let total_score: f64 = questions_arr.iter()
        .filter_map(|q| q["score"].as_f64())
        .sum();
    let max_score: f64 = questions_arr.iter()
        .filter_map(|q| q["max_score"].as_f64())
        .sum();
    let ai_confidence = question_details["confidence"].as_f64().unwrap_or(0.85);

    // 5. 存储批改结果
    let result_id = common::generate_id();
    sqlx::query(
        r#"
        INSERT INTO grading_results (id, submission_id, total_score, max_score, question_details, ai_confidence)
        VALUES ($1, $2, $3, $4, $5, $6)
        "#
    )
    .bind(&result_id)
    .bind(&task.submission_id)
    .bind(total_score)
    .bind(max_score)
    .bind(&question_details)
    .bind(ai_confidence)
    .execute(db)
    .await?;

    // 6. 提取并存储错题
    for question in questions_arr {
        if !question["is_correct"].as_bool().unwrap_or(true) {
            let wrong_answer_id = common::generate_id();
            let knowledge_points: Vec<String> = question["knowledge_points"]
                .as_array()
                .unwrap_or(&vec![])
                .iter()
                .filter_map(|v| v.as_str().map(String::from))
                .collect();

            sqlx::query(
                r#"
                INSERT INTO wrong_answers 
                (id, student_id, homework_id, grading_result_id, question_number, 
                 question_content, student_answer, correct_answer, error_type, knowledge_points, explanation)
                SELECT $1, $2, hs.homework_id, $3, $4, $5, $6, $7, $8, $9, $10
                FROM homework_submissions hs WHERE hs.id = $11
                "#
            )
            .bind(&wrong_answer_id)
            .bind(&student_id)
            .bind(&result_id)
            .bind(question["number"].as_i64().unwrap_or(0) as i32)
            .bind(question["question_content"].as_str().unwrap_or(""))
            .bind(question["student_answer"].as_str())
            .bind(question["correct_answer"].as_str().unwrap_or(""))
            .bind(question["error_type"].as_str())
            .bind(&knowledge_points)
            .bind(question["explanation"].as_str())
            .bind(&task.submission_id)
            .execute(db)
            .await?;
        }
    }

    // 7. 更新任务状态为完成
    sqlx::query(
        "UPDATE grading_tasks SET status = 'completed', completed_at = NOW() WHERE id = $1"
    )
    .bind(&task.id)
    .execute(db)
    .await?;

    // 8. 更新提交状态为已批改
    sqlx::query(
        "UPDATE homework_submissions SET status = 'graded' WHERE id = $1"
    )
    .bind(&task.submission_id)
    .execute(db)
    .await?;

    tracing::info!("Grading completed for task: {}, score: {}/{}", task.id, total_score, max_score);
    Ok(())
}

// ========== RabbitMQ 队列消费者 ==========

/// 启动批改任务队列消费者
async fn start_grading_consumer(db: PgPool, deepseek_api_key: String) -> anyhow::Result<()> {
    use lapin::{
        options::*, types::FieldTable, Connection, ConnectionProperties,
    };
    use futures::StreamExt;

    let rabbitmq_url = std::env::var("RABBITMQ_URL")
        .unwrap_or_else(|_| "amqp://guest:guest@localhost:5672".to_string());

    tracing::info!("Connecting to RabbitMQ: {}", rabbitmq_url);
    
    let conn = Connection::connect(&rabbitmq_url, ConnectionProperties::default()).await?;
    let channel = conn.create_channel().await?;

    // 声明批改队列
    let queue = channel
        .queue_declare(
            "grading_tasks",
            QueueDeclareOptions {
                durable: true,
                ..Default::default()
            },
            FieldTable::default(),
        )
        .await?;

    tracing::info!("Declared queue: {:?}", queue);

    // 设置预取数量（一次处理一个任务）
    channel
        .basic_qos(1, BasicQosOptions::default())
        .await?;

    // 开始消费消息
    let mut consumer = channel
        .basic_consume(
            "grading_tasks",
            "grading_consumer",
            BasicConsumeOptions::default(),
            FieldTable::default(),
        )
        .await?;

    tracing::info!("Grading consumer started, waiting for tasks...");

    // 轮询处理任务
    let mut interval = tokio::time::interval(std::time::Duration::from_secs(5));
    
    loop {
        tokio::select! {
            // RabbitMQ 消息
            delivery = consumer.next() => {
                if let Some(Ok(delivery)) = delivery {
                    let task_id = String::from_utf8_lossy(&delivery.data).to_string();
                    tracing::info!("Received grading task from queue: {}", task_id);

                    // 获取任务详情
                    if let Ok(Some(task)) = sqlx::query_as::<_, GradingTask>(
                        "SELECT * FROM grading_tasks WHERE id = $1 AND status = 'pending'"
                    )
                    .bind(&task_id)
                    .fetch_optional(&db)
                    .await
                    {
                        // 更新状态为处理中
                        sqlx::query("UPDATE grading_tasks SET status = 'processing', started_at = NOW() WHERE id = $1")
                            .bind(&task_id)
                            .execute(&db)
                            .await
                            .ok();

                        // 执行批改
                        match process_grading_task(&db, &task, &deepseek_api_key).await {
                            Ok(_) => {
                                tracing::info!("Task {} completed successfully", task_id);
                                delivery.ack(BasicAckOptions::default()).await.ok();
                            }
                            Err(e) => {
                                tracing::error!("Task {} failed: {}", task_id, e);
                                // 更新失败次数
                                let retry_count = task.retry_count + 1;
                                if retry_count >= 3 {
                                    sqlx::query(
                                        "UPDATE grading_tasks SET status = 'failed', error_message = $1 WHERE id = $2"
                                    )
                                    .bind(&format!("{}", e))
                                    .bind(&task_id)
                                    .execute(&db)
                                    .await
                                    .ok();
                                    delivery.ack(BasicAckOptions::default()).await.ok();
                                } else {
                                    sqlx::query(
                                        "UPDATE grading_tasks SET status = 'pending', retry_count = $1 WHERE id = $2"
                                    )
                                    .bind(retry_count)
                                    .bind(&task_id)
                                    .execute(&db)
                                    .await
                                    .ok();
                                    // Nack 让消息重新入队
                                    delivery.nack(BasicNackOptions { requeue: true, ..Default::default() }).await.ok();
                                }
                            }
                        }
                    }
                }
            }
            // 定期轮询数据库中的待处理任务（备用机制）
            _ = interval.tick() => {
                if let Ok(pending_tasks) = sqlx::query_as::<_, GradingTask>(
                    "SELECT * FROM grading_tasks WHERE status = 'pending' LIMIT 5"
                )
                .fetch_all(&db)
                .await
                {
                    for task in pending_tasks {
                        tracing::info!("Processing pending task from DB: {}", task.id);
                        sqlx::query("UPDATE grading_tasks SET status = 'processing', started_at = NOW() WHERE id = $1")
                            .bind(&task.id)
                            .execute(&db)
                            .await
                            .ok();

                        if let Err(e) = process_grading_task(&db, &task, &deepseek_api_key).await {
                            tracing::error!("Failed to process task {}: {}", task.id, e);
                            sqlx::query(
                                "UPDATE grading_tasks SET status = 'failed', error_message = $1 WHERE id = $2"
                            )
                            .bind(&format!("{}", e))
                            .bind(&task.id)
                            .execute(&db)
                            .await
                            .ok();
                        }
                    }
                }
            }
        }
    }
}

// ========== 主函数 ==========

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    use common::service::*;
    
    init_tracing();
    dotenvy::dotenv().ok();

    let db = init_db("DATABASE_URL", "postgresql://intelligrade:password@localhost:5432/intelligrade").await?;
    let deepseek_api_key = env_or("DEEPSEEK_API_KEY", "your_deepseek_api_key");
    let rabbitmq_url = env_or("RABBITMQ_URL", "amqp://guest:guest@localhost:5672");

    // 启动批改消费者
    let db_clone = db.clone();
    let api_key_clone = deepseek_api_key.clone();
    tokio::spawn(async move {
        if let Err(e) = start_grading_consumer(db_clone, api_key_clone).await {
            tracing::error!("Grading consumer error: {}", e);
        }
    });

    let app = Router::new()
        .route("/health", get(health_check))
        .route("/api/v1/grading/start", post(start_grading))
        .route("/api/v1/grading/result/:submission_id", get(get_grading_result))
        .route("/api/v1/grading/result/:result_id/review", patch(review_grading))
        .route("/api/v1/grading/wrong-answers/:student_id", get(get_wrong_answers))
        .with_state(Arc::new(AppState { db, deepseek_api_key, rabbitmq_url }));

    start_service("Grading service", &env_or("GRADING_SERVICE_PORT", "8083"), app).await
}
